#include <string.h>
#include <stdlib.h>

#include "lib.h"
#include "crapssql.h"
#include "crround.h"
#include "crbet.h"
#include "crapsave.h"
#include "odbc.h"
#include "missinggamenums.h"
#include "opts.h"
#include "stdncerr.h"
#include "crapgame.h"

CrapsSQL::CrapsSQL(CrapsConnection *pConn, TrackSQL *pSQL)
    : GameDBInterface(pConn, pSQL)
{
    // kharmon.  06-16-06.  #3077.  SicBo implementation.
	// purposefully set max vals < min vals to disallow playing
	// games where these values don't come from database
	tableLimits = new CTableLimits(
		500,
		1,
		500,
		1,
		500,
		1,
		500,
		1,
		500,
		1,
		500,
		1,
		25,
		2,
		0);

#ifdef USE_MISSINGGAMENUMS
    //missing game nums deleted in TrackSQL::~TrackSQL
    missinggamenums = new MissingGameNums(this, "pub_getTableIdentityCraps");
#endif
}

CrapsSQL::~CrapsSQL()
{
    delete tableLimits;
    tableLimits = NULL;
}


// kharmon.  06-16-06.  #3077.  SicBo implementation.
// called when the play initially connect to load the table limits and any other game options
bool8 CrapsSQL::trackLoadOptions()
{
    CrapsConnection::Info *info = m_pConn->getCasinoSpecificInfo();
    int32 result = TrackSQL::ExecFailed;

    if (m_pTrackSQL && info)
    {
        m_pTrackSQL->lock();
        result = getPlayerLimits(info->pid, CRAPS_GAME_ID, 0, tableLimits);
        m_pTrackSQL->unlock();
    }

    return (bool8)(result == TrackSQL::NoError);
}

// called to save the current roll results to the database.
bool8 CrapsSQL::trackRoll(uint8 point, uint8 die1, uint8 die2,
                    CrapsRound *round, CrapsRound *current, 
                    int32 *betAmounts, int32 *bonusPayAmounts,
                    int32 totalWinningBets, int32 continueBets, int32 continueBonusBets,
                    int32 payout, int32 bonusPayout,
                    int32 *balance, int32 *compBalance, int32 *bonusBalance)
{
    CrapsConnection::Info *info = m_pConn->getCasinoSpecificInfo();
    int32 result = TrackSQL::ExecFailed;

    if (m_pTrackSQL && info)
    {
        m_pTrackSQL->lock();
        result = roll(ServerOpts::casinoId,
            info->pid, info->gid, info->forMoney,
            info->sessionId, info->forMoney ? tableLimits->m_compFactor : 0,
            point, die1, die2,
            round, current, betAmounts, bonusPayAmounts,
            totalWinningBets, continueBets, continueBonusBets,
            payout, bonusPayout, 
            balance, compBalance, bonusBalance);
        m_pTrackSQL->unlock();
    }

    return (bool8)(result == TrackSQL::NoError);
}


bool8 CrapsSQL::trackScrubBet(uint8 betID)
{
    CrapsConnection::Info *info = m_pConn->getCasinoSpecificInfo();
    int32 result = TrackSQL::ExecFailed;
    
    if (m_pTrackSQL && info)
    {
        m_pTrackSQL->lock();
        result = scrubBet(info->gid, betID, info->pid);
        m_pTrackSQL->unlock();
    }

    return (result == TrackSQL::NoError);
}

bool8 CrapsSQL::trackHasSavedGame(DiceGame *& pGame, int32 *err)
{
    CrapsConnection::Info *info = m_pConn->getCasinoSpecificInfo();
    CrapsSavedGame sg;
    int32 result=0;
    bool8 exists=false;

    if (m_pTrackSQL && info)
    {
        m_pTrackSQL->lock();

        result = hasSavedGame(info->pid, info->gid, info->forMoney, &sg, &exists);

        if (result == TrackSQL::NoError && exists)
        {
            pGame = new CrapsGame(m_pConn, &sg);
            if (pGame)
            {
                m_pTrackSQL->unlock();
                return true;
            }
            m_pTrackSQL->unlock();

            *err = LOGINERR_CONTACTCUSTSERVICE;
            return false;
        }
        else
        {
            memset(info->gid, 0xff, sizeof(CrapsGameID));

            if (TrackSQL::NoError == result)
            {
                m_pTrackSQL->unlock();

                *err = 0;
                return false;            // all ok, no saved game
            }
        }

        m_pTrackSQL->unlock();
    }
    *err = LOGINERR_CONTACTCUSTSERVICE;

    return false;
}


// protected method that does the actual work of formating the data and executing the resulting query.
int32
CrapsSQL::roll(int32 casinoId,
    const PID& pid, CrapsGameID gid, bool8 forMoney, SESSIONID sessionId,
    int32 compFactor,
    uint8 point, uint8 die1, uint8 die2,
    CrapsRound *round, CrapsRound *current,
    int32 *betAmounts, int32 *bonusPayAmounts,
    int32 totalWinningBets, int32 continueBets, int32 continueBonusBets,
    int32 payout, int32 bonusPayout,
    int32 *balance, int32 *compBalance, int32 *bonusBalance)
{
    ODBCConnection *conn;
    ODBCQuery *query;
    bool8 ok = false;

    conn = m_pTrackSQL->getConn();
    if (!conn)
        return TrackSQL::NoConnection;

    String betbuf;

    query = conn->query();
    if (query)
    {
        query->define("exec pub_CreateGameCraps ");
        query->addParam(casinoId);
        query->addParam(pid);
        query->addParam((bool8)(forMoney != 0));
        query->addParam(sessionId, ODBCQuery::NOQUOTES);
        query->addParam(compFactor);
        query->addParam(point);
        query->addParam(die1);
        query->addParam(die2);
        query->addParam(round->numMoneyBets());
        query->addParam(round->totalBet(), ODBCQuery::MONEY);
        query->addParam(continueBets, ODBCQuery::MONEY);
        query->addParam(payout, ODBCQuery::MONEY);
        query->addParam(round->totalBonusBet(), ODBCQuery::MONEY);
        query->addParam(continueBonusBets, ODBCQuery::MONEY);
        query->addParam(bonusPayout, ODBCQuery::MONEY);


        if (query->exec())
        {
            while (query->getRow())
            {
                ok =
                    query->getData(1, gid, CRAPS_GAMEID_SIZE) &&
                    query->getData(2, balance, ODBCQuery::MONEY) &&
                    query->getData(3, compBalance) &&
                    query->getData(4, bonusBalance, ODBCQuery::MONEY);
            }
        }

        if (ok && query->reset())
        {
            CrapsBet *inBet, *curBet;
            uint8 i;

            for (i=0; i < CrapsBet::NUMBETS; i++)
            {
                inBet = round->getBet(i);
                curBet = current->getBet(i);
                if (inBet && curBet && ((inBet->getAmount() > 0) || (curBet->getAmount() > 0)) )
                {
                    query->define("insert CrapsBets(gamenum,betID,incomingAmount,currentAmount,state, incomingBonusBet, currentBonusBet, payBonus)"
                        " values(");
                    query->addParam(gid, ODBCQuery::NOQUOTES);
                    query->addParam(inBet->getBetID());
                    query->addParam(inBet->getAmount(), ODBCQuery::MONEY);
                    query->addParam(curBet->getAmount(), ODBCQuery::MONEY);
                    query->addParam(betAmounts[i]);
                    query->addParam(inBet->getBonusAmount(), ODBCQuery::MONEY);
                    query->addParam(curBet->getBonusAmount(), ODBCQuery::MONEY);
                    query->addParam(bonusPayAmounts[i], ODBCQuery::MONEY);
                    query->append(") ");

                    betbuf += query->getQuery();
                }
            }

            ok = false;

            if (query->exec(betbuf))
            {
                ok = true;

                while (query->getRow())
                    /* ignore */ ;
            }
        }

        delete query;
    }
    delete conn;

    if (ok)
    {
        #ifdef USE_MISSINGGAMENUMS
        int32 gamenum = atol(gid);
        if (missinggamenums)
                missinggamenums->setGameNum(gamenum);
        #endif
    }

    return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

int32
CrapsSQL::getPlayerLimits(const PID& pid, int32 gameId, uint8 machId,
        CTableLimits *limits)
{
    ODBCConnection* conn;
    ODBCQuery* query;
    bool8 ok = false;

    conn = m_pTrackSQL->getConn();
    if (!conn)
        return TrackSQL::NoConnection;

    query = conn->query();
    if (query)
    {
        query->define("exec pub_lim_getPlayerLimits ");
        query->addParam(pid);
        query->addParam(gameId);
        query->addParam(machId, ODBCQuery::BINARY);

        if (query->exec())
        {
            while (query->getRow())
            {
                ok =
                    query->getData(4,  &limits->m_minPassSet, ODBCQuery::MONEY) &&
                    query->getData(5,  &limits->m_maxPassSet, ODBCQuery::MONEY) &&
                    query->getData(6,  &limits->m_minOddsSet, ODBCQuery::MONEY) &&
                    query->getData(7,  &limits->m_maxOddsSet, ODBCQuery::MONEY) &&
                    query->getData(8,  &limits->m_minPlaceSet, ODBCQuery::MONEY) &&
                    query->getData(9,  &limits->m_maxPlaceSet, ODBCQuery::MONEY) &&
                    query->getData(10, &limits->m_minHardPropsSet, ODBCQuery::MONEY) &&
                    query->getData(11, &limits->m_maxHardPropsSet, ODBCQuery::MONEY) &&
                    query->getData(12, &limits->m_minFieldBigSet, ODBCQuery::MONEY) &&
                    query->getData(13, &limits->m_maxFieldBigSet, ODBCQuery::MONEY) &&
                    query->getData(14, &limits->m_minTable, ODBCQuery::MONEY) &&
                    query->getData(15, &limits->m_maxTable, ODBCQuery::MONEY) &&
                    query->getData(16, &limits->m_compFactor);
            }
        }

        if (ok && query->reset())
        {
            query->define("exec pub_CrapsGetOptions ");

            if (query->exec())
            {
                while (query->getRow())
                {
                    ok =
                        query->getData(1, &limits->m_betOdds) &&
                        query->getData(2, &limits->m_field12Odds);
                }
            }
        }

        delete query;
    }
    delete conn;

    return ok ? m_pTrackSQL->isGameEnabled(gameId,machId) : TrackSQL::ExecFailed;
}



int32
CrapsSQL::hasSavedGame(const PID& pid, CrapsGameID gid, bool8 forMoney,
                       CrapsSavedGame *sg, bool8 *exists)
{
    ODBCConnection *conn;
    ODBCQuery *query;
    bool8 ok = false;
    int32 hasPlayed = 0;

    *exists = false;

    conn = m_pTrackSQL->getConn();
    if (!conn)
        return TrackSQL::NoConnection;


    query = conn->query();
    if (query)
    {
        query->define("exec pub_CrapsGetSavedGame ");
        query->addParam(pid);
        query->addParam((bool8)(forMoney != 0));

        if (query->exec())
        {
            while (query->getRow())
            {
                if (query->getData(1, &hasPlayed) && hasPlayed)
                {
                    ok =
                        query->getData(2, gid, CRAPS_GAMEID_SIZE) &&
                        query->getData(3, &sg->point) &&
                        query->getData(4, &sg->die1) &&
                        query->getData(5, &sg->die2);
                }
            }
        }

        if (ok && hasPlayed && query->reset())
        {
            uint8 betID;

            query->define("exec pub_CrapsGetSavedBets ");
            query->addParam(gid, ODBCQuery::NOQUOTES);

            if (query->exec())
            {
                while (query->getRow())
                {
                    ok =
                        query->getData(1, &betID) &&
                        query->getData(2, &sg->bets.round[betID], ODBCQuery::MONEY) &&
                        query->getData(3, &sg->bets.current[betID], ODBCQuery::MONEY) &&
                        query->getData(4, &sg->bets.state[betID]) &&
                        query->getData(5, &sg->bets.bonusRound[betID], ODBCQuery::MONEY) &&
                        query->getData(6, &sg->bets.bonusCurrent[betID], ODBCQuery::MONEY);

                    // if there is at least one positive value for the current bets,
                    // or if the point is 'on',
                    // then this 'saved game' should be restored
                    if (!(*exists) &&
                        ((sg->bets.current[betID] > 0) || (sg->point != 0) ) )
                            *exists = true;

                }
            }


        }
        delete query;
    }
    delete conn;

    if (hasPlayed)
        return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
    else
        return TrackSQL::NoError;
}


int32 CrapsSQL::scrubBet(CrapsGameID gid, uint8 betID, const PID& pid)
{
    ODBCConnection *conn;
    ODBCQuery *query;
    bool8 ok = false;

    conn = m_pTrackSQL->getConn();
    if (!conn)
        return TrackSQL::NoConnection;

    query = conn->query();
    if (query)
    {
        query->define("EXEC pub_CrapsScrubBet ");
        query->addParam(gid, ODBCQuery::NOQUOTES);
        query->addParam(betID);
        query->addParam(pid);

        if (query->exec())
        {
            if (query->getRow())
                ok = true;
        }

        delete query;
    }
    delete conn;

    return ok ? TrackSQL::NoError : TrackSQL::ExecFailed;
}

// CSN-5666: tool to force craps results
String CrapsSQL::getForcedRolls(const PID& pid, int32 gameId, uint8 machId)
{
    ODBCConnection *conn;
    ODBCQuery *query;
	String sResult = "";

    conn = m_pTrackSQL->getConn();
    if (!conn)
        return sResult;

    query = conn->query();
    if (query)
    {
		String sQuery;
        sQuery.sprintf( "SELECT dices FROM ForcedRoll WHERE gameId = %d AND machId = %d AND PID = '%S'", gameId, machId, (const wchar_t*)pid);     
		query->define(sQuery);
        if (query->exec())
        {
            if (query->getRow())
				query->getData(1, sResult);
        }

        delete query;
    }
    delete conn;

    return sResult;
}